package cn.wolfcode.p2p.base.service.impl;

import cn.wolfcode.p2p.base.domain.LoginInfo;
import cn.wolfcode.p2p.base.mapper.LoginInfoMapper;
import cn.wolfcode.p2p.base.service.IAccountService;
import cn.wolfcode.p2p.base.service.ILoginInfoService;
import cn.wolfcode.p2p.base.service.IUserInfoService;
import cn.wolfcode.p2p.base.vo.LoginInfoVO;
import cn.wolfcode.p2p.base.vo.VerifyCodeVO;
import cn.wolfcode.p2p.business.service.IExpAccountService;
import cn.wolfcode.p2p.enums.ErrorCode;
import cn.wolfcode.p2p.hosting.service.IHostingService;
import cn.wolfcode.p2p.util.Assert;
import cn.wolfcode.p2p.util.DateUtil;
import cn.wolfcode.p2p.util.MD5;
import cn.wolfcode.p2p.util.UserContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.text.MessageFormat;
import java.util.Date;
import java.util.List;

import static cn.wolfcode.p2p.util.Constants.*;

@Service
public class LoginInfoServiceImpl implements ILoginInfoService {

    @Autowired
    private LoginInfoMapper loginInfoMapper;
    @Autowired
    private IAccountService accountService;
    @Autowired
    private IUserInfoService userInfoService;
    @Autowired
    private IHostingService hostingService;
    @Autowired
    private IExpAccountService expAccountService;

    /**
     * 注册用户信息:形参传入LoginInfoVO类的封装信息
     * mapper调用LoginInfo新增操作,在操作之前把LoginInfo类信息设置成LoginInfoVO类
     * 注意:密码需要加盐加密 使用MD5工具类(密码+用户名)进行编码
     *
     * @param loginInfoVO
     */
    @Override
    public void userRegister(LoginInfoVO loginInfoVO) {
        LoginInfo loginInfo = new LoginInfo();
        loginInfo.setNickName(loginInfoVO.getNickName());
        loginInfo.setUsername(loginInfoVO.getUsername());
        //密码加盐加密
        String encode = MD5.encode(loginInfoVO.getPassword() + loginInfoVO.getUsername());
        loginInfo.setPassword(encode);
        //注册前的校验方法
        registerCheck(loginInfoVO);
        loginInfoMapper.insert(loginInfo);
        //初始化账户对象
        accountService.init(loginInfo);
        //初始化个人信息对象
        userInfoService.init(loginInfo);

        //初始化体验金账户
        expAccountService.init(loginInfo);


        //创建一个托管账户
        //hostingService.createAccount(loginInfo);

    }

    private void registerCheck(LoginInfoVO loginInfoVO) {
        //判断昵称4-6位
        Assert.isRangeLength(loginInfoVO.getNickName(), LENGTH_MIN_NICKNAME, LENGTH_MAX_NICKNAME,
                MessageFormat.format(ErrorCode.LOGININFO_NICKNAME_LENGTH_ERROR.getErrorMessage(), LENGTH_MIN_NICKNAME, LENGTH_MAX_NICKNAME));

        //判断手机号是否已注册
        Assert.isFalse(existUsername(loginInfoVO.getUsername()), ErrorCode.LOGININFO_USERNAME_EXIST_ERROR.getErrorMessage());
        //判断手机号格式
        Assert.isPhoneNum(loginInfoVO.getUsername(), ErrorCode.LOGININFO_USERNAME_FORMAT_ERROR.getErrorMessage());

        //判断密码的长度
        Assert.isRangeLength(loginInfoVO.getPassword(), LENGTH_MIN_PASSWORD, LENGTH_MAX_PASSWORD,
                MessageFormat.format(ErrorCode.LOGININFO_PASSWORD_LENGTH_ERROR.getErrorMessage(), LENGTH_MIN_PASSWORD, LENGTH_MAX_PASSWORD));
        //判断确认密码的长度
        Assert.isRangeLength(loginInfoVO.getConfirmPwd(), LENGTH_MIN_PASSWORD, LENGTH_MAX_PASSWORD, ErrorCode.LOGININFO_CONFIRM_PASSWORD_EQUALS_ERROR.getErrorMessage());
        //判断确认密码是否相等于密码
        Assert.isEquals(loginInfoVO.getPassword(), loginInfoVO.getConfirmPwd(), ErrorCode.LOGININFO_CONFIRM_PASSWORD_EQUALS_ERROR.getErrorMessage());

        //判断验证码长度
        Assert.isLength(loginInfoVO.getVerifycode(), LENGTH_VERIFYCODE, MessageFormat.format(ErrorCode.LOGININFO_VERIFYCODE_LENGTH_ERROR.getErrorMessage(), LENGTH_VERIFYCODE));

        //判断接收验证码手机号和注册提交的手机号要一致, 防止人修改
        VerifyCodeVO codeVO = UserContext.getVerifycodeVo();
        Assert.notNull(codeVO, ErrorCode.LOGININFO_VERIFYCODE_NULL.getErrorMessage());
        Assert.isEquals(codeVO.getPhoneNum(), loginInfoVO.getUsername(), "接收验证码的手机号和当前注册手机号不一致");

        //验证码准确性校验
        Assert.isEquals(codeVO.getCode(), loginInfoVO.getVerifycode(), "无效的验证码");

        //验证码过期校验:发送验证码的时间和当前注册时间的间隔不能超过5分钟(300秒)
        Assert.isFalse(DateUtil.getSecondsBetween(codeVO.getSendTime(), new Date()) > 300, "验证码已失效,请重新发送");
    }

    /**
     * 校验数据库是否存在这一条信息
     * 如果大于0即存在信息,返回true
     *
     * @param username
     * @return
     */
    @Override
    public boolean existUsername(String username) {
        return loginInfoMapper.getCountByUsername(username) > 0;
    }

    /**
     * 用户登录
     *
     * @param loginInfo
     */
    @Override
    public void login(LoginInfo loginInfo, int userType) {
        //参数校验
        loginCheck(loginInfo.getUsername(), loginInfo.getPassword(), userType);
        //执行登录:通过用户名和密码去数据库中查询一个登录对象
        LoginInfo login = loginInfoMapper.getByUsernameAndPassword(loginInfo.getUsername(), MD5.encode(loginInfo.getPassword() + loginInfo.getUsername()));
        //是否登录成功,抛出异常
        Assert.notNull(login, "用户名和密码有误");
        //判断前台后台用户的类型,防止串门
        Assert.isFalse(userType != login.getUserType(), "非法的用户类型");
        //判断用户状态是否已经被锁定
        Assert.isFalse(LoginInfo.STATUS_DISABLE == login.getState(), "该账户已经冻结,请联系客服");
        //把登录对象放入session
        UserContext.setLoginInfo(login);
    }

    /**
     * 登录校验
     * 用usertype 区分是前台登录还是后台登录
     *
     * @param username
     * @param password
     */
    public void loginCheck(String username, String password, int userType) {
        if (userType == LoginInfo.USERTYPE_WEBSITE) {
            //判断手机号格式
            Assert.isPhoneNum(username, ErrorCode.LOGININFO_USERNAME_FORMAT_ERROR.getErrorMessage());
        } else {
            Assert.notNull(username, "用户名不可为空");
        }
        //判断密码的长度
        Assert.isRangeLength(password, LENGTH_MIN_PASSWORD, LENGTH_MAX_PASSWORD,
                MessageFormat.format(ErrorCode.LOGININFO_PASSWORD_LENGTH_ERROR.getErrorMessage(), LENGTH_MIN_PASSWORD, LENGTH_MAX_PASSWORD));
    }

    /**
     * 创建管理员
     *
     * @param admin
     */
    @Override
    public void insert(LoginInfo admin) {
        loginInfoMapper.insert(admin);
    }

    /**
     * 查询所有的审核员
     *
     * @return
     */
    @Override
    public List<LoginInfo> listAuditors() {
        return loginInfoMapper.selectAuditors(LoginInfo.AUDITOR_YES);
    }

}
